#!/usr/bin/env bash
set -e

export BATS_VERSION='1.1.0'

version() {
  printf 'Bats %s\n' "$BATS_VERSION"
}

abort() {
  printf 'Error: %s\n' "$1" >&2
  usage >&2
  exit 1
}

usage() {
  local cmd="${0##*/}"
  local line

  while IFS= read -r line; do
    printf '%s\n' "$line"
  done <<END_OF_HELP_TEXT
Usage: $cmd [-cr] [-f <regex>] [-j <jobs>] [-p | -t] <test>...
       $cmd [-h | -v]

  <test> is the path to a Bats test file, or the path to a directory
  containing Bats test files (ending with ".bats").

  -c, --count      Count the number of test cases without running any tests
  -f, --filter     Filter test cases by names matching the regular expression
  -h, --help       Display this help message
  -j, --jobs       Number of parallel jobs to run (requires GNU parallel)
  -p, --pretty     Show results in pretty format (default for terminals)
  -r, --recursive  Include tests in subdirectories
  -t, --tap        Show results in TAP format
  -v, --version    Display the version number

  For more information, see https://github.com/bats-core/bats-core

END_OF_HELP_TEXT
}

expand_link() {
  readlink="$(type -p greadlink readlink | head -1)"
  "$readlink" -f "$1"
}

expand_path() {
  local path="${1%/}"
  local dirname="${path%/*}"
  local result="$2"

  if [[ "$dirname" == "$path" ]]; then
    dirname="$PWD"
  else
    cd "$dirname"
    dirname="$PWD"
    cd "$OLDPWD"
  fi
  printf -v "$result" '%s/%s' "$dirname" "${path##*/}"
}

BATS_LIBEXEC="$(dirname "$(expand_link "$BASH_SOURCE")")"
export BATS_CWD="$PWD"
export BATS_TEST_PATTERN="^[[:blank:]]*@test[[:blank:]]+(.*[^[:blank:]])[[:blank:]]+\{(.*)\$"
export BATS_TEST_FILTER=
export PATH="$BATS_LIBEXEC:$PATH"

arguments=()

# Unpack single-character options bundled together, e.g. -cr, -pr.
for arg in "$@"; do
  if [[ "$arg" =~ ^-[^-]. ]]; then
    index=1
    while option="${arg:$((index++)):1}"; do
      if [[ -z "$option" ]]; then
        break
      fi
      arguments+=("-$option")
    done
  else
    arguments+=("$arg")
  fi
  shift
done

set -- "${arguments[@]}"
arguments=()

unset flags pretty recursive
flags=()
pretty=
recursive=
if [[ -z "${CI:-}" && -t 0 && -t 1 ]] && command -v tput >/dev/null; then
  pretty=1
fi

while [[ "$#" -ne 0 ]]; do
  case "$1" in
  -h|--help)
    version
    usage
    exit 0
    ;;
  -v|--version)
    version
    exit 0
    ;;
  -c|--count)
    flags+=('-c')
    ;;
  -f|--filter)
    shift
    flags+=('-f' "$1")
    ;;
  -j|--jobs)
    shift
    flags+=('-j' "$1")
    ;;
  -r|--recursive)
    recursive=1
    ;;
  -t|--tap)
    pretty=
    ;;
  -p|--pretty)
    pretty=1
    ;;
  -*)
    abort "Bad command line option '$1'"
    ;;
  *)
    arguments+=("$1")
    ;;
  esac
  shift
done

if [[ "${#arguments[@]}" -eq 0 ]]; then
  abort 'Must specify at least one <test>'
fi

filenames=()
for filename in "${arguments[@]}"; do
  expand_path "$filename" 'filename'

  if [[ -d "$filename" ]]; then
    shopt -s nullglob
    if [[ "$recursive" -eq 1 ]]; then
      while IFS= read -r -d $'\0' file; do
        filenames+=("$file")
      done < <(find "$filename" -type f -name '*.bats' -print0 | sort -z)
    else
      for suite_filename in "$filename"/*.bats; do
        filenames+=("$suite_filename")
      done
    fi
    shopt -u nullglob
  else
    filenames+=("$filename")
  fi
done

formatter="cat"
if [[ -n "$pretty" ]]; then
  flags+=("-x")
  formatter="bats-format-tap-stream"
fi

set -o pipefail execfail
exec bats-exec-suite "${flags[@]}" "${filenames[@]}" | "$formatter"
